package cz.muni.fi.pa165.dao.impl;

import cz.muni.fi.pa165.dao.DeliveryDAO;
import cz.muni.fi.pa165.entity.Customer;
import cz.muni.fi.pa165.entity.Delivery;
import cz.muni.fi.pa165.entity.Employee;
import cz.muni.fi.pa165.entity.Package;
import cz.muni.fi.pa165.shiro.ShiroSecured;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresRoles;

/**
 * @author Ondrej Cernay
 */
@Stateless
@ShiroSecured
@RequiresAuthentication
public class DeliveryDAOImpl implements DeliveryDAO {

    @PersistenceContext(unitName = "pa165")
    private EntityManager em;

    private void checkDeliveryAttributes(Delivery delivery) {

        if (delivery.getCustomer() == null) {
            throw new IllegalArgumentException("Customer is null");
        }
        if (delivery.getCustomer().getId() == null) {
            throw new IllegalArgumentException("Customer's id is null");
        }
        if (delivery.getRecipientName() == null) {
            throw new IllegalArgumentException("Recipient's name is null");
        }
        if (delivery.getRecipientCity() == null) {
            throw new IllegalArgumentException("Recipient's city is null");
        }
        if (delivery.getRecipientAddress() == null) {
            throw new IllegalArgumentException("Recipient's address is null");
        }
        if (delivery.getRecipientZip() == null) {
            throw new IllegalArgumentException("Recipient's zip is null");
        }
        if (delivery.getCreationDate() != null && delivery.getDeliveryDate() != null && delivery.getCreationDate().after(delivery.getDeliveryDate())) {
            throw new IllegalArgumentException("Delivery date cannot be in the past.");
        }
    }

    @RequiresRoles("Manager")
    @Override
    public void createDelivery(Delivery delivery) {
        if (delivery == null || delivery.getId() != null) {
            throw new IllegalArgumentException("Delivery is null or ID is already set");
        }

        // ak nie je nastaveny datum vytvorenia, tak ho nastavime na aktualny cas
        if (delivery.getCreationDate() == null) {
            delivery.setCreationDate(new Date());
        }

        if (delivery.getPackages() == null) {
            delivery.setPackages(new ArrayList<Package>());
        }

        checkDeliveryAttributes(delivery);

        em.persist(delivery);
    }

    @RequiresRoles("Manager")
    @Override
    public void removeDelivery(Delivery delivery) {
        if (delivery == null) {
            throw new IllegalArgumentException("Delivery is null.");
        }
        if (delivery.getId() == null) {
            throw new IllegalArgumentException("Delivery id is null.");
        }

        em.remove(em.find(Delivery.class, delivery.getId()));
    }

    @Override
    public void updateDelivery(Delivery delivery) {
        if (delivery == null || delivery.getId() == null) {
            throw new IllegalArgumentException("Delivery is null or id is null.");
        }
        if (delivery.getDeliveryDate() != null) {
            if (!delivery.getCreationDate().before(delivery.getDeliveryDate())) {
                throw new IllegalArgumentException("Creation date must be before delivery date.");
            }
        }
        checkDeliveryAttributes(delivery);

        em.merge(delivery);
    }

    @Override
    public List<Delivery> getAllDeliveries() {
        List<Delivery> result = em.createQuery("SELECT d FROM Delivery d ORDER BY d.creationDate DESC").getResultList();

        return result;
    }

    @Override
    public Delivery getDeliveryById(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("Id is null.");
        }

        try {
            return (Delivery) em.createQuery("SELECT d FROM Delivery d WHERE d.id=:id")
                    .setParameter("id", id)
                    .getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }

    @Override
    public List<Delivery> getDeliveriesByDeliveryBoy(Employee deliveryBoy) {
        if (deliveryBoy == null) {
            throw new IllegalArgumentException("DeliveryBoy is null");
        }

        if (deliveryBoy.getId() == null) {
            throw new IllegalArgumentException("DeliveryBoy's id is null");
        }

        return em.createQuery("SELECT d FROM Delivery d WHERE d.deliveryBoy.id=:id")
                .setParameter("id", deliveryBoy.getId()).getResultList();
    }

    @Override
    public List<Delivery> getDeliveriesByCustomer(Customer customer) {
        if (customer == null) {
            throw new IllegalArgumentException("Customer is null");
        }

        if (customer.getId() == null) {
            throw new IllegalArgumentException("Customer's id is null");
        }

        return em.createQuery("SELECT d FROM Delivery d WHERE d.customer.id=:id")
                .setParameter("id", customer.getId()).getResultList();
    }

    @Override
    public List<Delivery> getDeliveriesByDeliveryDate(Date from, Date to) {
        if (from == null || to == null) {
            throw new IllegalArgumentException("A paramter is null");
        }

        if (from.compareTo(to) > 0) {
            throw new IllegalArgumentException("Date from is greater than date to");
        }

        return em.createQuery("SELECT d FROM Delivery d WHERE d.deliveryDate BETWEEN :from AND :to")
                .setParameter("from", from)
                .setParameter("to", to)
                .getResultList();
    }

    @Override
    public List<Delivery> getDeliveriesByCreationDate(Date from, Date to) {
        if (from == null || to == null) {
            throw new IllegalArgumentException("A paramter is null");
        }

        if (from.compareTo(to) > 0) {
            throw new IllegalArgumentException("Date from is greater than date to");
        }

        return em.createQuery("SELECT d FROM Delivery d WHERE d.creationDate BETWEEN :from AND :to")
                .setParameter("from", from)
                .setParameter("to", to)
                .getResultList();
    }

    @Override
    public List<Delivery> getDeliveriesByRecipient(String name) {
        if (name == null) {
            throw new IllegalArgumentException("Parameter is null");
        }

        return em.createQuery("SELECT d FROM Delivery d WHERE d.recipientName LIKE :name")
                .setParameter("name", "%" + name + "%").getResultList();
    }

    @Override
    public List<Delivery> getDeliveriesByAddress(String address) {
        if (address == null) {
            throw new IllegalArgumentException("Parameter is null");
        }

        return em.createQuery("SELECT d FROM Delivery d WHERE d.recipientAddress LIKE :address")
                .setParameter("address", "%" + address + "%").getResultList();
    }

    @Override
    public List<Delivery> getDeliveriesByZip(String zip) {
        if (zip == null) {
            throw new IllegalArgumentException("Parameter is null");
        }

        return em.createQuery("SELECT d FROM Delivery d WHERE d.recipientZip LIKE :zip")
                .setParameter("zip", "%" + zip + "%").getResultList();
    }

    @Override
    public List<Delivery> getDeliveriesByCity(String city) {
        if (city == null) {
            throw new IllegalArgumentException("Parameter is null");
        }

        return em.createQuery("SELECT d FROM Delivery d WHERE d.recipientCity LIKE :city")
                .setParameter("city", "%" + city + "%").getResultList();
    }
}
